/**
 * Title:       AgentManagementService.java
 * Copyright:   Copyright (c) 1996-2004 The Agent Factory Working Group. All rights reserved.
 * Licence:     This file is free software; you can redistribute it and/or modify
 *              it under the terms of the GNU Lesser General Public License as published by
 *              the Free Software Foundation; either version 2.1, or (at your option)
 *              any later version.
 *
 *              This file is distributed in the hope that it will be useful,
 *              but WITHOUT ANY WARRANTY; without even the implied warranty of
 *              MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *              GNU Lesser General Public License for more details.
 *
 *              You should have received a copy of the GNU Lesser General Public License
 *              along with Agent Factory; see the file COPYING.  If not, write to
 *              the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 *              Boston, MA 02111-1307, USA.
 *
 */
package com.agentfactory.service.ams;

import com.agentfactory.platform.core.InterpreterDescriptor;
import com.agentfactory.platform.util.Logger;
import com.agentfactory.platform.service.PlatformServiceDescriptor;
import com.agentfactory.platform.service.PlatformServiceManager;
import com.agentfactory.platform.service.PlatformService;
import com.agentfactory.service.ams.AgentDescription;
import com.agentfactory.service.ams.LocalAgentDescription;
import com.agentfactory.platform.core.Agent;
import com.agentfactory.platform.AgentPlatform;

import java.util.*;
import java.lang.reflect.Constructor;

/**
 * Implementation of the FIPA HTTP Message Transport Service.
 *
 * @author  Rem Collier
 */
public class AgentManagementService extends PlatformService implements Observer {
    private static final int CLASS_LOG_LEVEL = Logger.LEVEL_WARNING;
    
    private PlatformServiceManager manager;
    private AgentPlatform platform;
    private ArrayList registry;
    private String portAuthorityAgent;
    private ArrayList unregisteredAgents;
    
    public void init(PlatformServiceDescriptor descriptor, PlatformServiceManager manager) {
        this.manager = manager;
        platform = manager.getAgentPlatform();
        registry = new ArrayList(); 
        unregisteredAgents = new ArrayList(); 
    }
    
    public synchronized void registerAgent(AgentDescription description) {
        if(unregisteredAgents.contains(description)){
            unregisteredAgents.remove(description);
        }
        registry.add(description);
    }
    
    public synchronized void deregisterAgent(AgentDescription description) {
        registry.remove(description);
    }
    
    public synchronized ArrayList getAgentNames() {
        ArrayList list = new ArrayList();
        
        AgentDescription agent = null;
        for (int i=0; i < registry.size(); i++) {
            agent = (AgentDescription) registry.get(i);
            list.add(agent.getName().getName());
        }
        
        return list;
    }
    
    public synchronized ArrayList getUnregisteredAgents() {
        ArrayList list = new ArrayList();
        
        AgentDescription agent = null;
        for (int i=0; i < unregisteredAgents.size(); i++) {
            agent = (AgentDescription) unregisteredAgents.get(i);
            list.add(agent);
        }
        
        return list;
    }
    
    public synchronized void removeUnregisteredAgent(AgentDescription agentD) {        
        unregisteredAgents.remove(agentD);        
        Agent agent = null;

    }
    
    public synchronized ArrayList getLocalAgents() {
        return platform.getAgentContainer().getAgents();
    }
    
    public synchronized boolean modify(String oldName, String newName) {
        return platform.getAgentContainer().modify(oldName, newName);
    }
    
    public synchronized AgentDescription search(String name) {
        AgentDescription agent = null;
        
        boolean found = false;
        int index = 0;
        while ((index < registry.size()) && !found) {
            agent = (AgentDescription) registry.get(index++);
            found = agent.getName().getName().equals(name);
        }
        
        return agent;
    }
    
    public synchronized boolean resumeAgent(String name) {
        return platform.getAgentContainer().resume(name);
    }
    
    public synchronized boolean suspendAgent(String name) {
        return platform.getAgentContainer().suspend(name);
    }
    
    public synchronized boolean terminateAgent(String name) {
        AgentDescription ad = search(name);
        deregisterAgent(ad);
        return platform.getAgentContainer().terminate(name);
    }
    
    public synchronized void terminatePlatform() {
        System.out.println("Shutting Down Agent Platform");
        platform.destrory();
    }
    
    public synchronized Agent getAgent(String name) {
        return platform.getAgentContainer().get(name);
    }
    
    public synchronized String getPortAuthorityAgent(){
        return portAuthorityAgent;
    }
    
    public synchronized void setPortAuthorityAgent(String portAuthorityAgent){
        this.portAuthorityAgent=portAuthorityAgent;
    }
    
    public void update(Observable o, Object arg) {
    }
    
    public synchronized void notify(String message) {
        setChanged();
        notifyObservers(message);
    }
    
    public void bind(Agent agent) {
    }
    
    public void modifyBinding(String oldName, String name) {
    }
    
    public void start() {
    }
    
    public void unbind(Agent agent) {
    }
    
    /**
     * Create an agent based on an agent description file.
     *
     * Currently, agents can be created from .afapl code files. .agt is no
     * longer directly supported.
     *
     * @param inName -
     *            A String Object. The name of the agent that is to be
     *            constructed.sout
     * @param inType -
     *            A String Object, the name of the code file that is to be used
     *            to construct the agent.
     * @throws AgentConfigurationException -
     *             thrown if there is a problem constructing an agent.
     */
    public synchronized Agent createAgent(String inName, String inType) {
        Logger.enter("createAgent(" + inName + "," + inType + ",platform)", CLASS_LOG_LEVEL);
        
        Agent theAgent = null;
        int lastIndex = inType.lastIndexOf(".");
        String extension = inType.substring(lastIndex + 1, inType.length());
        
        /**
         * There are two choices here:
         *
         * - Option 1: We are specifying a Java class that implements an
         *             agent architecture.
         * - Option 2: We are specifying a text file that should be loaded
         *             into a given agent architecture (selected based on
         *             the extension of the text file).
         */
        if (extension.equals("java")) {
            String theClass = inType.substring(0, lastIndex).replaceAll("/", ".");
            Class[] types = { String.class, AgentPlatform.class };
            Object[] params = { inName, platform };
            try {
                Class nextClass = Class.forName(theClass);
                Constructor constructor = nextClass.getConstructor(types);
                theAgent = (Agent) constructor.newInstance(params);
            } catch (Exception e) {
                System.out.println( "[AgentManagementService] failed to create agent: " + inName );
                e.printStackTrace();
            }
        } else {
            // Now check if there is an interpreter that will take care of this
            // design type
            InterpreterDescriptor descriptor = (InterpreterDescriptor) platform.getInterpreters().get(extension);
            if ( descriptor == null ) {
                System.out.println("[AgentManagementService] Could not create agent: " + inName + " as this extension type is not supported.");
                return null;
            }
            
            String plugin = descriptor.getInterpreterClass();
            if (plugin == null) {
                throw new RuntimeException(
                    "No interpreter plugins have been loaded for code of type: : "
                    + extension);
            } else {
                // got something. Initialise with this plugin.
                Logger.detail("Creating an " + extension + " Agent with plugin " + plugin,
                CLASS_LOG_LEVEL);

                // Use factory instantiation on the agent interpreter.
                Class[] types = { String.class, String.class, AgentPlatform.class, Vector.class };
                Object[] params = { inName, inType, platform, descriptor.getArguements() };
                try {
                    Class nextClass = Class.forName(plugin);

                    Constructor constructor = nextClass.getConstructor(types);
                    theAgent = (Agent) constructor.newInstance(params);
                } catch (Exception e) {
                    System.out.println( "[AgentManagementService] failed to create agent: " + inName );
                    e.printStackTrace();
                }
            }
        }

        theAgent.metaRegistration();
        platform.getAgentContainer().add(theAgent, inName);
        LocalAgentDescription desc = new LocalAgentDescription(theAgent);
        unregisteredAgents.add(desc);
        // Make the agent an observer of the agent platform so that it can
        // receive any relevant events...
        platform.addObserver(theAgent);
        this.addObserver(theAgent);
        
        return theAgent;
    }
    
    public synchronized Agent createAgentFromDesign(String inName, String agentDesign, String inType) {
        Logger.enter("createAgent(" + inName + "," + inType + ",platform)", CLASS_LOG_LEVEL);
        Agent theAgent = null;
        String extension = new String();
        if(inType.equals("afapl")){
            extension = new String("agt");
        } else if(inType.equals("afapl2")){
            extension = new String("agent");
        }
        InterpreterDescriptor descriptor = (InterpreterDescriptor) platform.getInterpreters().get(extension);
        String plugin = descriptor.getInterpreterClass();
        if (plugin != null) {
            Logger.detail("Creating an " + extension + " Agent with plugin " + plugin,
            CLASS_LOG_LEVEL);
            
            // Use factory instantiation on the agent interpreter.
            Class[] types = { String.class, String.class, StringBuffer.class, AgentPlatform.class, Vector.class };
            Object[] params = { inName, inType, new StringBuffer(agentDesign), platform, descriptor.getArguements() };
            try {               
                Class nextClass = Class.forName(plugin);
                Constructor constructor = nextClass.getConstructor(types);
                theAgent = (Agent) constructor.newInstance(params);
                theAgent.metaRegistration();
            } catch (Exception e) {
                e.printStackTrace();
                throw new RuntimeException("When constructing agent : "+ inType +" could not find the constructor for: " + plugin);
            }            
            platform.getAgentContainer().add(theAgent, inName);
            LocalAgentDescription desc = new LocalAgentDescription(theAgent);
            unregisteredAgents.add(desc);
            platform.addObserver(theAgent);
            this.addObserver(theAgent);
        } else {
            throw new RuntimeException(
            "No interpreter plugins have been loaded for code of type: : "
            + extension);
        }
        return theAgent;
    }
}
